package com.xiaoshi.oauth.login;


import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.google.common.reflect.TypeToken;
import com.xiaoshi.interfaces.Constants;
import com.xiaoshi.interfaces.ConstantsRedis;
import com.xiaoshi.utils.ObjectUtils;
import com.xiaoshi.utils.RedisUtils;
import com.xiaoshi.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author sen
 */
@Slf4j
@Service
public class RedisClientDetailsServiceImpl extends JdbcClientDetailsService {


    @Autowired
    private RedisUtils redisUtils;

    /**
     * 多数据原的时候  使用
     */
    private static DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtils.getBean(DynamicRoutingDataSource.class);

    public RedisClientDetailsServiceImpl(DataSource dataSource) {
        super(dynamicRoutingDataSource.getDataSource(Constants.dataSourceName.DATA_SOURCE_NAME_ONE));
    }


    @Override
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        ClientDetails clientDetails = redisGetClientDetail(clientId);
        if (ObjectUtils.isEmpty(clientDetails) || ObjectUtils.isEmpty(clientDetails.getClientId())) {
            clientDetails = cacheAndGetClient(clientId);
        }
        return clientDetails;
    }

    public ClientDetails redisGetClientDetail(String clientId) throws InvalidClientException {
        BaseClientDetails clientDetails = new BaseClientDetails();
        if (redisUtils.hasKey(ConstantsRedis.SysKey.SYS_CLIENDS_FIND_ALL_)) {

            Type type = new TypeToken<ArrayList<com.xiaoshi.system.entity.oauth.ClientDetails>>() {}.getType();
            List<com.xiaoshi.system.entity.oauth.ClientDetails> clientDetailsList = redisUtils.lGetAll(ConstantsRedis.SysKey.SYS_CLIENDS_FIND_ALL_,type);

            if(ObjectUtils.isNotEmpty(clientDetailsList)){
                com.xiaoshi.system.entity.oauth.ClientDetails myclientDetails =
                        clientDetailsList.stream().filter(o -> ObjectUtils.equals(o.getClientId(), clientId)).findFirst().get();

                if (ObjectUtils.isNotEmpty(myclientDetails)) {
                    BeanUtils.copyProperties(myclientDetails, clientDetails);
                    if (ObjectUtils.isNotEmpty(myclientDetails.getAuthorizedGrantTypes()))
                        clientDetails.setAuthorizedGrantTypes(Arrays.stream(myclientDetails.getAuthorizedGrantTypes().split(",")).collect(Collectors.toList()));
                    if (ObjectUtils.isNotEmpty(myclientDetails.getAccessTokenValidity()))
                        clientDetails.setAccessTokenValiditySeconds(myclientDetails.getAccessTokenValidity().intValue());
                    if (ObjectUtils.isNotEmpty(myclientDetails.getRefreshTokenValidity()))
                        clientDetails.setRefreshTokenValiditySeconds(myclientDetails.getRefreshTokenValidity().intValue());
                    if (ObjectUtils.isNotEmpty(myclientDetails.getScope()))
                        clientDetails.setScope(Arrays.stream(myclientDetails.getScope().split(",")).collect(Collectors.toList()));
                    if (ObjectUtils.isNotEmpty(myclientDetails.getAutoapprove()))
                        clientDetails.setAutoApproveScopes(Arrays.stream(myclientDetails.getAutoapprove().split(",")).collect(Collectors.toList()));
                    if (ObjectUtils.isNotEmpty(myclientDetails.getAuthorities()))
                        clientDetails.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(myclientDetails.getAuthorities()));
                    if (ObjectUtils.isNotEmpty(myclientDetails.getWebServerRedirectUri())) {

                        clientDetails.setRegisteredRedirectUri(Arrays.stream(myclientDetails.getWebServerRedirectUri().split(",")).collect(Collectors.toSet()));
                    }

                }
            }



        }
        return clientDetails;
    }


    /**
     * 缓存 client并返回 client
     *
     * @param clientId clientId
     */
    public ClientDetails cacheAndGetClient(String clientId) {
        ClientDetails clientDetails = null;
        clientDetails = super.loadClientByClientId(clientId);
        if (clientDetails != null) {
            BaseClientDetails baseClientDetails = (BaseClientDetails) clientDetails;
            Set<String> autoApproveScopes = baseClientDetails.getAutoApproveScopes();
            if (ObjectUtils.isNotEmpty(autoApproveScopes)) {
                baseClientDetails.setAutoApproveScopes(
                        autoApproveScopes.stream().map(this::convert).collect(Collectors.toSet())
                );
            }
            com.xiaoshi.system.entity.oauth.ClientDetails myclientDetail = new com.xiaoshi.system.entity.oauth.ClientDetails();
            BeanUtils.copyProperties(baseClientDetails, myclientDetail);
            myclientDetail.setScope(baseClientDetails.getScope().stream().collect(Collectors.joining(",")));
            myclientDetail.setAutoapprove(baseClientDetails.getAutoApproveScopes().stream().collect(Collectors.joining(",")));
            myclientDetail.setAuthorizedGrantTypes(baseClientDetails.getAuthorizedGrantTypes().stream().collect(Collectors.joining(",")));
            if (ObjectUtils.isNotEmpty(baseClientDetails.getAuthorities()))
                myclientDetail.setAuthorities(baseClientDetails.getAuthorities().stream().map(o -> o.getAuthority()).collect(Collectors.joining(",")));
            if (ObjectUtils.isNotEmpty(baseClientDetails.getRegisteredRedirectUri())) {
                myclientDetail.setWebServerRedirectUri(baseClientDetails.getRegisteredRedirectUri().stream().collect(Collectors.joining()));
            }

            redisUtils.lSet(ConstantsRedis.SysKey.SYS_CLIENDS_FIND_ALL_, myclientDetail);
        }
        return clientDetails;
    }

    private String convert(String value) {
        return value;
    }
}
